using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using TrueCode.DependencyManager.BuildActions.Injection;
using TrueCode.DependencyManager.Interface;

namespace TrueCode.DependencyManager.Services.Parameters
{
  public class WeakTypedParameterProvider : IParameterProvider
  {
    private readonly object[] _parameters;

    public WeakTypedParameterProvider( params object[] parameters )
    {
      if( parameters.Length == 0 ) throw new ArgumentNullException( "parameters", "No parameters specified" );
      _parameters = parameters;
    }

    #region Implementation of IParameterProvider
    public object[] GetValuesFor( IBuildSession context, ParameterInfo[] parameterInfo )
    {
      var values = new object[parameterInfo.Length];

      for( var i = 0; i < values.Length; i++ )
      {
        values[ i ] = GetValue( parameterInfo[ i ].ParameterType ) ??
                      context.MakeCopy( parameterInfo[ i ].ParameterType ).Build();
      }
      return values;
    }
    #endregion

    private object GetValue( Type requestedType )
    {
      var parameters = _parameters.
        Where( param =>
                 {
                   var typedValue = param as TypedValue;
                   return typedValue == null
                            ? requestedType.IsAssignableFrom( param.GetType() )
                            : typedValue.Type == requestedType;
                 } ).
        Select( param =>
                  {
                    var typedValue = param as TypedValue;
                    return typedValue == null ? param : typedValue.Value;
                  } );

      #region Assert that there is no parameters abmbiguity
      if( parameters.Count() > 1 )
      {
        var exception =
          new InvalidOperationException( "Parameters ambiguity, more than one parameter is assignable to requested type" );
        exception.Data.Add( "Type of method parameter", requestedType );

        var counter = 0;
        foreach( var parameter in parameters )
          exception.Data.Add( "Proper parameter type" + counter++, parameter.GetType() );
        throw exception;
      }
      #endregion

      return parameters.FirstOrDefault();
    }
  }
}